#pragma once

#include<iostream>
#include<unistd.h>
#include<cstring>
#include<cstdlib>
#include<string>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<functional>
#include"log.hpp"

using namespace std;

#define SIZE 1024

using func_t = function<std::string (const std::string&)>;

Log log;

enum
{
    SOC_CREA_ERROR = 1,
    SOC_BIND_ERROR = 2,
    SOC_RECV_ERROE = 3,
    SOC_SEND_ERROR
};

const uint16_t defaultport = 8080;
//ipbind为0, 只要是该主机的ip都可以接收
const string defaultip = "0.0.0.0";

class UdpServer
{
public:
    UdpServer(const string& ip = defaultip, const uint16_t& port = defaultport)
    :_sockfd(0), _ip(ip), _port(port), _isrunning(false)
    {}

    void init()
    {
        //1. server创建套接字文件udo socket
       _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            log(Fatal, "socket file create fail:%d, %s", errno, strerror(errno));
            exit(SOC_CREA_ERROR);
        }

        log(Info, "socket file create success:%d, %s    ", errno, strerror(errno));

        //2. server绑定端口号: a.创建struct sockaddr_in对象, 并且对他赋值 b.调用bind将数据设置进入内核
        struct sockaddr_in local;
        socklen_t len = sizeof(local);
        bzero(&local, len);
        local.sin_family = AF_INET; 
        local.sin_port = htons(_port); //需要将port转换为网络字节序
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); //1)将string转换成int 2)将int类型的_ip转换为网络字节序
        
        int n = bind(_sockfd, (struct sockaddr*)&local, len);
        if(n < 0)
        {
            log(Fatal, "socket file bind fail:%d, %s", errno, strerror(errno));
            exit(SOC_BIND_ERROR);
        }

        log(Info, "socket file bind success, socket fd:%d", _sockfd);

    }

    void run(func_t func)
    {
        _isrunning = true;

        //string buffer;
        char inbuffer[SIZE];

        while(_isrunning)
        {
            //1. server接受client发送的数据: a.定义缓冲区存放数据 b.定义struct sockaddr_in作为输出参数确定是哪个client发送的数据
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            bzero(&client, len);

            //如果成功返回收到字节数
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if(n < 0)
            {
                log(Fatal, "recvce fail:%d, %s", errno, strerror(errno));
                exit(SOC_RECV_ERROE);
            }

            inbuffer[n] = 0;
            //2.对数据进行加工处理
            string echo = inbuffer;
            string echo_string = func(inbuffer);

            //3. 将数据发送回客户端
            ssize_t m = sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&client, len);
            if(m < 0)
            {
                log(Fatal, "send fail:%d, %s", errno, strerror(errno));
                exit(SOC_SEND_ERROR);
            }
        }
    }

    ~UdpServer()
    {
        if(_sockfd > 0)
        {
            close(_sockfd);
        }
    }

private:
    int _sockfd; //用于打开套接字文件的文件描述符
    string _ip; //服务器端的ip地址
    uint16_t _port; //服务器端进程端口号
    bool _isrunning;
};

